{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模块和包\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 模块\n",
    "* 包\n",
    "* 发布模块\n",
    "\n",
    "## 01. 模块\n",
    "\n",
    "### 1.1 模块的概念\n",
    "\n",
    "> **模块是 Python 程序架构的一个核心概念**\n",
    "\n",
    "* 每一个以扩展名 `py` 结尾的 `Python` 源代码文件都是一个 **模块**\n",
    "* **模块名** 同样也是一个 **标识符**，需要符合标识符的命名规则\n",
    "* 在模块中定义的 **全局变量** 、**函数**、**类** 都是提供给外界直接使用的 **工具**\n",
    "* **模块** 就好比是 **工具包**，要想使用这个工具包中的工具，就需要先 **导入** 这个模块\n",
    "\n",
    "### 1.2 模块的两种导入方式\n",
    "\n",
    "#### 1）import 导入\n",
    "\n",
    "```python\n",
    "import 模块名1, 模块名2 \n",
    "```\n",
    "\n",
    "> 提示：在导入模块时，每个导入应该独占一行\n",
    "\n",
    "```python\n",
    "import 模块名1\n",
    "import 模块名2 \n",
    "```\n",
    "\n",
    "* **导入之后**\n",
    "    * 通过 `模块名.` 使用 **模块提供的工具** —— **全局变量**、**函数**、**类**\n",
    "\n",
    "##### 使用 `as` 指定模块的别名\n",
    "\n",
    "> **如果模块的名字太长**，可以使用 `as` 指定模块的名称，以方便在代码中的使用\n",
    "\n",
    "```python\n",
    "import 模块名1 as 模块别名\n",
    "```\n",
    "\n",
    "> 注意：**模块别名** 应该符合 **大驼峰命名法**\n",
    "\n",
    "#### 2）from...import 导入\n",
    "\n",
    "* 如果希望 **从某一个模块** 中，导入 **部分** 工具，就可以使用 `from ... import` 的方式\n",
    "* `import 模块名` 是 **一次性** 把模块中 **所有工具全部导入**，并且通过 **模块名/别名** 访问\n",
    "\n",
    "```python\n",
    "# 从 模块 导入 某一个工具\n",
    "from 模块名1 import 工具名\n",
    "```\n",
    "\n",
    "* 导入之后\n",
    "    * **不需要** 通过 `模块名.`\n",
    "    * 可以直接使用 **模块提供的工具** —— **全局变量**、**函数**、**类**\n",
    "\n",
    "**注意**\n",
    "\n",
    "> 如果 **两个模块**，存在 **同名的函数**，那么 **后导入模块的函数**，会 **覆盖掉先导入的函数**\n",
    "\n",
    "* 开发时 `import` 代码应该统一写在 **代码的顶部**，更容易及时发现冲突\n",
    "* 一旦发现冲突，可以使用 `as` 关键字 **给其中一个工具起一个别名**\n",
    "\n",
    "##### from...import *（知道）\n",
    "\n",
    "```python\n",
    "# 从 模块 导入 所有工具\n",
    "from 模块名1 import *\n",
    "```\n",
    "\n",
    "**注意**\n",
    "\n",
    "> 这种方式不推荐使用，因为函数重名并没有任何的提示，出现问题不好排查\n",
    "\n",
    "### 1.3 模块的搜索顺序[扩展]\n",
    "\n",
    "`Python` 的解释器在 **导入模块** 时，会：\n",
    "\n",
    "1. 搜索 **当前目录** 指定模块名的文件，**如果有就直接导入**\n",
    "2. 如果没有，再搜索 **系统目录** \n",
    "\n",
    "> 在开发时，给文件起名，不要和 **系统的模块文件** **重名**\n",
    "\n",
    "`Python` 中每一个模块都有一个内置属性 `__file__` 可以 **查看模块** 的 **完整路径**\n",
    "\n",
    "**示例**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "\n",
    "# 生成一个 0～10 的数字\n",
    "rand = random.randint(0, 10)\n",
    "\n",
    "print(rand)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 注意：如果当前目录下，存在一个 `random.py` 的文件，程序就无法正常执行了！\n",
    "\n",
    "* 这个时候，`Python` 的解释器会 **加载当前目录** 下的 `random.py` 而不会加载 **系统的** `random` 模块\n",
    "\n",
    "### 1.4 原则 —— 每一个文件都应该是可以被导入的\n",
    "\n",
    "* 一个 **独立的 `Python` 文件** 就是一个 **模块**\n",
    "* 在导入文件时，文件中 **所有没有任何缩进的代码** 都会被执行一遍！\n",
    "\n",
    "**实际开发场景**\n",
    "\n",
    "* 在实际开发中，每一个模块都是独立开发的，大多都有专人负责\n",
    "* **开发人员** 通常会在 **模块下方** **增加一些测试代码**\n",
    "    * 仅在模块内使用，而被导入到其他文件中不需要执行\n",
    "\n",
    "#### `__name__` 属性\n",
    "\n",
    "> * `__name__` 属性可以做到，测试模块的代码 **只在测试情况下被运行**，而在 **被导入时不会被执行**！\n",
    "\n",
    "* `__name__` 是 `Python` 的一个内置属性，记录着一个 **字符串**\n",
    "* 如果 **是被其他文件导入的**，`__name__` 就是 **模块名**\n",
    "* 如果 **是当前执行的程序** `__name__` 是 **`__main__`**\n",
    "\n",
    "**在很多 `Python` 文件中都会看到以下格式的代码**：\n",
    "\n",
    "```python\n",
    "# 导入模块\n",
    "# 定义全局变量\n",
    "# 定义类\n",
    "# 定义函数\n",
    "\n",
    "# 在代码的最下方\n",
    "def main():\n",
    "    # ...\n",
    "    pass\n",
    "\n",
    "# 根据 __name__ 判断是否执行下方代码\n",
    "if __name__ == \"__main__\":\n",
    "    main()\n",
    "\n",
    "```\n",
    "\n",
    "## 02. 包（Package）\n",
    "\n",
    "### 概念\n",
    "\n",
    "* **包** 是一个 **包含多个模块** 的 **特殊目录**\n",
    "* 目录下有一个 **特殊的文件** `__init__.py`\n",
    "* 包名的 **命名方式** 和变量名一致，**小写字母** + `_`\n",
    "\n",
    "**好处**\n",
    "\n",
    "* 使用 `import 包名` 可以一次性导入 **包** 中 **所有的模块**\n",
    "\n",
    "### 案例演练\n",
    "\n",
    "1. 新建一个 `hm_message` 的 **包**\n",
    "2. 在目录下，新建两个文件 `send_message` 和 `receive_message`\n",
    "3. 在 `send_message` 文件中定义一个 `send` 函数\n",
    "4. 在 `receive_message` 文件中定义一个 `receive` 函数\n",
    "5. 在外部直接导入 `hm_message` 的包\n",
    "\n",
    "### `__init__.py`\n",
    "\n",
    "* 要在外界使用 **包** 中的模块，需要在 `__init__.py` 中指定 **对外界提供的模块列表**\n",
    "\n",
    "```python\n",
    "# 从 当前目录 导入 模块列表\n",
    "from . import send_message\n",
    "from . import receive_message\n",
    "```\n",
    "\n",
    "## 03. 发布模块（知道）\n",
    "\n",
    "* 如果希望自己开发的模块，**分享** 给其他人，可以按照以下步骤操作\n",
    "\n",
    "### 3.1 制作发布压缩包步骤\n",
    "\n",
    "#### 1) 创建 setup.py\n",
    "\n",
    "* `setup.py` 的文件\n",
    "\n",
    "```python\n",
    "from distutils.core import setup\n",
    "\n",
    "setup(name=\"hm_message\",  # 包名\n",
    "      version=\"1.0\",  # 版本\n",
    "      description=\"itheima's 发送和接收消息模块\",  # 描述信息\n",
    "      long_description=\"完整的发送和接收消息模块\",  # 完整描述信息\n",
    "      author=\"itheima\",  # 作者\n",
    "      author_email=\"itheima@itheima.com\",  # 作者邮箱\n",
    "      url=\"www.itheima.com\",  # 主页\n",
    "      py_modules=[\"hm_message.send_message\",\n",
    "                  \"hm_message.receive_message\"])\n",
    "\n",
    "```\n",
    "\n",
    "有关字典参数的详细信息，可以参阅官方网站：\n",
    "\n",
    "https://docs.python.org/2/distutils/apiref.html\n",
    "\n",
    "#### 2) 构建模块\n",
    "\n",
    "```bash\n",
    "$ python3 setup.py build\n",
    "```\n",
    "\n",
    "#### 3) 生成发布压缩包\n",
    "\n",
    "```bash\n",
    "$ python3 setup.py sdist\n",
    "```\n",
    "\n",
    "> 注意：要制作哪个版本的模块，就使用哪个版本的解释器执行！\n",
    "\n",
    "### 3.2 安装模块\n",
    "\n",
    "```bash\n",
    "$ tar -zxvf hm_message-1.0.tar.gz \n",
    "\n",
    "$ sudo python3 setup.py install\n",
    "```\n",
    "\n",
    "**卸载模块**\n",
    "\n",
    "直接从安装目录下，把安装模块的 **目录** 删除就可以\n",
    "\n",
    "```python\n",
    "$ cd /usr/local/lib/python3.5/dist-packages/\n",
    "$ sudo rm -r hm_message*\n",
    "```\n",
    "\n",
    "### 3.3 `pip` 安装第三方模块\n",
    "\n",
    "* **第三方模块** 通常是指由 **知名的第三方团队** **开发的** 并且被 **程序员广泛使用** 的 `Python` 包 / 模块\n",
    "    * 例如 `pygame` 就是一套非常成熟的 **游戏开发模块**\n",
    "* `pip` 是一个现代的，通用的 `Python` 包管理工具\n",
    "* 提供了对 `Python` 包的查找、下载、安装、卸载等功能\n",
    "\n",
    "安装和卸载命令如下：\n",
    "\n",
    "```bash\n",
    "# 将模块安装到 Python 2.x 环境\n",
    "$ sudo pip install pygame\n",
    "$ sudo pip uninstall pygame\n",
    "\n",
    "# 将模块安装到 Python 3.x 环境\n",
    "$ sudo pip3 install pygame\n",
    "$ sudo pip3 uninstall pygame\n",
    "```\n",
    "\n",
    "#### 在 `Mac` 下安装 `iPython`\n",
    "\n",
    "```bash\n",
    "$ sudo pip install ipython\n",
    "```\n",
    "\n",
    "#### 在 `Linux` 下安装 `iPython`\n",
    "\n",
    "```bash\n",
    "$ sudo apt install ipython\n",
    "$ sudo apt install ipython3\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
